package com.jneagle.xlstool.bzwgjmx.impl.dao;

import com.dwarfeng.subgrade.sdk.interceptor.analyse.BehaviorAnalyse;
import com.dwarfeng.subgrade.sdk.interceptor.analyse.SkipRecord;
import com.dwarfeng.subgrade.stack.bean.dto.PagingInfo;
import com.dwarfeng.subgrade.stack.bean.key.LongIdKey;
import com.dwarfeng.subgrade.stack.exception.DaoException;
import com.jneagle.xlstool.bzwgjmx.stack.bean.entity.RawPartData;
import com.jneagle.xlstool.bzwgjmx.stack.dao.RawPartDataDao;
import com.jneagle.xlstool.bzwgjmx.stack.service.RawPartDataMaintainService;
import org.dozer.Mapper;
import org.springframework.stereotype.Repository;

import java.util.*;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@Repository
public class RawPartDataDaoImpl implements RawPartDataDao {

    private final Mapper mapper;

    private final Map<LongIdKey, RawPartData> memory = new LinkedHashMap<>();

    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Map<LongIdKey, List<RawPartData>> childForRawPartIndex = new HashMap<>();

    private boolean indexReadyFlag = false;

    public RawPartDataDaoImpl(Mapper mapper) {
        this.mapper = mapper;
    }

    @Override
    @BehaviorAnalyse
    public void clear() {
        lock.writeLock().lock();
        try {
            memory.clear();
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public boolean exists(LongIdKey key) {
        lock.readLock().lock();
        try {
            return memory.containsKey(key);
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public RawPartData get(LongIdKey key) throws DaoException {
        lock.readLock().lock();
        try {
            return cloneBean(memory.get(key));
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public LongIdKey insert(RawPartData element) throws DaoException {
        lock.writeLock().lock();
        try {
            indexReadyFlag = false;
            RawPartData neoElement = cloneBean(element);
            memory.put(neoElement.getKey(), neoElement);
            return neoElement.getKey();
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public void update(RawPartData element) throws DaoException {
        lock.writeLock().lock();
        try {
            indexReadyFlag = false;
            RawPartData neoElement = cloneBean(element);
            memory.put(neoElement.getKey(), neoElement);
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public void delete(LongIdKey key) {
        lock.writeLock().lock();
        try {
            indexReadyFlag = false;
            memory.keySet().remove(key);
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public boolean allExists(@SkipRecord List<LongIdKey> keys) {
        lock.readLock().lock();
        try {
            return memory.keySet().containsAll(keys);
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public boolean nonExists(@SkipRecord List<LongIdKey> keys) {
        lock.readLock().lock();
        try {
            for (LongIdKey key : keys) {
                if (memory.containsKey(key)) {
                    return false;
                }
            }
            return true;
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    @SkipRecord
    public List<RawPartData> batchGet(@SkipRecord List<LongIdKey> keys) throws DaoException {
        lock.readLock().lock();
        try {
            List<RawPartData> result = new ArrayList<>();
            for (LongIdKey key : keys) {
                RawPartData rawPartData = cloneBean(memory.get(key));
                result.add(rawPartData);
            }
            return result;
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    @SkipRecord
    public List<LongIdKey> batchInsert(@SkipRecord List<RawPartData> elements) throws DaoException {
        lock.writeLock().lock();
        try {
            indexReadyFlag = false;
            List<LongIdKey> result = new ArrayList<>();
            for (RawPartData element : elements) {
                RawPartData neoElement = cloneBean(element);
                memory.put(neoElement.getKey(), neoElement);
                result.add(neoElement.getKey());
            }
            return result;
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public void batchUpdate(@SkipRecord List<RawPartData> elements) throws DaoException {
        lock.writeLock().lock();
        try {
            indexReadyFlag = false;
            for (RawPartData element : elements) {
                RawPartData neoElement = cloneBean(element);
                memory.put(neoElement.getKey(), neoElement);
            }
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public void batchDelete(@SkipRecord List<LongIdKey> keys) {
        indexReadyFlag = false;
        lock.writeLock().lock();
        try {
            keys.forEach(memory.keySet()::remove);
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    @SkipRecord
    public List<RawPartData> lookup() throws DaoException {
        lock.readLock().lock();
        try {
            List<RawPartData> result = new ArrayList<>();
            for (RawPartData rawPartData : memory.values()) {
                result.add(cloneBean(rawPartData));
            }
            return result;
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    @SkipRecord
    public List<RawPartData> lookup(PagingInfo pagingInfo) throws DaoException {
        lock.readLock().lock();
        try {
            int beginIndex = pagingInfo.getPage() * pagingInfo.getRows();
            int endIndex = beginIndex + pagingInfo.getRows();
            List<RawPartData> subList = new ArrayList<>(memory.values()).subList(beginIndex, endIndex);
            List<RawPartData> result = new ArrayList<>();
            for (RawPartData rawPartData : subList) {
                result.add(cloneBean(rawPartData));
            }
            return result;
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public int lookupCount() {
        lock.readLock().lock();
        try {
            return memory.size();
        } finally {
            lock.readLock().unlock();
        }
    }

    @SuppressWarnings("SwitchStatementWithTooFewBranches")
    @Override
    public List<RawPartData> lookup(String preset, Object[] objs) throws DaoException {
        lock.readLock().lock();
        try {
            mayBuildIndex();
            switch (preset) {
                case RawPartDataMaintainService.CHILD_FOR_RAW_PART:
                    return childForRawPart(preset, objs);
                default:
                    throw new DaoException("未知的预设: " + preset);
            }
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    public List<RawPartData> lookup(String preset, Object[] objs, PagingInfo pagingInfo) throws DaoException {
        throw new DaoException("not implemented yet");
    }

    @SuppressWarnings("SwitchStatementWithTooFewBranches")
    @Override
    public int lookupCount(String preset, Object[] objs) throws DaoException {
        lock.readLock().lock();
        try {
            mayBuildIndex();
            switch (preset) {
                case RawPartDataMaintainService.CHILD_FOR_RAW_PART:
                    return childForRawPartCount(preset, objs);
                default:
                    throw new DaoException("未知的预设: " + preset);
            }
        } finally {
            lock.readLock().unlock();
        }
    }

    private List<RawPartData> childForRawPart(String preset, Object[] objs) throws DaoException {
        try {
            LongIdKey rawPartKey = (LongIdKey) objs[0];
            List<RawPartData> rawPartDataList = childForRawPartIndex.getOrDefault(rawPartKey, Collections.emptyList());
            List<RawPartData> result = new ArrayList<>();
            for (RawPartData rawPartData : rawPartDataList) {
                result.add(cloneBean(rawPartData));
            }
            return result;
        } catch (Exception e) {
            throw new DaoException("非法的参数, 预设: " + preset + " 参数: " + Arrays.toString(objs));
        }
    }

    private int childForRawPartCount(String preset, Object[] objs) throws DaoException {
        try {
            LongIdKey rawPartKey = (LongIdKey) objs[0];
            return childForRawPartIndex.getOrDefault(rawPartKey, Collections.emptyList()).size();
        } catch (Exception e) {
            throw new DaoException("非法的参数, 预设: " + preset + " 参数: " + Arrays.toString(objs));
        }
    }

    private void mayBuildIndex() {
        if (indexReadyFlag) {
            return;
        }

        childForRawPartIndex.clear();
        for (RawPartData rawPartData : memory.values()) {
            LongIdKey rawPartKey = rawPartData.getRawPartKey();
            if (childForRawPartIndex.containsKey(rawPartKey)) {
                childForRawPartIndex.get(rawPartKey).add(rawPartData);
            } else {
                List<RawPartData> list = new ArrayList<>();
                list.add(rawPartData);
                childForRawPartIndex.put(rawPartKey, list);
            }
        }

        indexReadyFlag = true;
    }

    private RawPartData cloneBean(RawPartData rawPartData) throws DaoException {
        try {
            return mapper.map(rawPartData, RawPartData.class);
        } catch (Exception e) {
            throw new DaoException(e);
        }
    }
}
